//
//  THttpTool.m
//  TProject
//
//  Created by txj on 2016/12/23.
//  Copyright © 2016年 txj. All rights reserved.
//

#import "THttpTool.h"
#import "AFNetworkActivityIndicatorManager.h"
#import <CommonCrypto/CommonDigest.h>
#import "AFNetworking.h"

//解决nslog打印不全
#ifdef DEBUG

#define NSLog(FORMAT, ...) fprintf(stderr, "%s:%zd\t%s\n", [[[NSString stringWithUTF8String: __FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat: FORMAT, ## __VA_ARGS__] UTF8String]);

#else

#define NSLog(FORMAT, ...) nil

#endif



@interface NSString (md5)

+ (NSString *)hybnetworking_md5:(NSString *)string;

@end

@implementation NSString (md5)

+ (NSString *)hybnetworking_md5:(NSString *)string {
    if (string == nil || [string length] == 0) {
        return nil;
    }
    
    unsigned char digest[CC_MD5_DIGEST_LENGTH], i;
    CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);
    NSMutableString *ms = [NSMutableString string];
    
    for (i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [ms appendFormat:@"%02x", (int)(digest[i])];
    }
    
    return [ms copy];
}

@end


@interface THttpTool()
@property (strong,nonatomic) AFHTTPSessionManager *mgr;
@end


@implementation THttpTool

#pragma mark - 变量定义
static NSString *sg_privateNetworkBaseUrl = nil;
static TNetworkStatus sg_networkStatus = kTNetworkStatusReachableViaWiFi;
static TResponseType sg_responseType = kTResponseTypeJSON;
static TRequestType  sg_requestType  = kTRequestTypePlainText;

static BOOL sg_shoulObtainLocalWhenUnconnected = NO;
static BOOL sg_cacheGet = YES;
static BOOL sg_cachePost = NO;
static BOOL sg_shouldAutoEncode = NO;
static BOOL sg_shouldCallbackOnCancelRequest = YES;
static BOOL sg_isBaseURLChanged = YES;

static NSUInteger sg_maxCacheSize = 0;

static NSTimeInterval sg_timeout = 20.0f;

static NSDictionary *sg_httpHeaders = nil;
static NSMutableArray *sg_requestTasks;

static AFHTTPSessionManager *sg_sharedManager = nil;


#pragma mark - 全局设置
+ (void)configRequestType:(TRequestType)requestType
             responseType:(TResponseType)responseType
      shouldAutoEncodeUrl:(BOOL)shouldAutoEncode
  callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest
{
    sg_requestType = requestType;
    sg_responseType = responseType;
    sg_shouldAutoEncode = shouldAutoEncode;
    sg_shouldCallbackOnCancelRequest = shouldCallbackOnCancelRequest;
}

+ (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders {
    sg_httpHeaders = httpHeaders;
}

+ (void)updateBaseUrl:(NSString *)baseUrl {
    if (![baseUrl isEqualToString:sg_privateNetworkBaseUrl] && baseUrl && baseUrl.length) {
        sg_isBaseURLChanged = YES;
    } else {
        sg_isBaseURLChanged = NO;
    }
    
    sg_privateNetworkBaseUrl = baseUrl;
}

+ (NSString *)baseUrl {
    return sg_privateNetworkBaseUrl;
}

+ (void)setTimeout:(NSTimeInterval)timeout {
    sg_timeout = timeout;
}


+ (TURLSessionTask *)getWithUrl:(NSString *)url
                     refreshCache:(BOOL)refreshCache
                          success:(TRequestSuccessBlock)success
                             fail:(TRequestFailureBlock)fail {
    return [self getWithUrl:url
               refreshCache:refreshCache
                     params:nil
                    success:success
                       fail:fail];
}

+ (TURLSessionTask *)getWithUrl:(NSString *)url
                     refreshCache:(BOOL)refreshCache
                           params:(NSDictionary *)params
                          success:(TRequestSuccessBlock)success
                             fail:(TRequestFailureBlock)fail {
    return [self getWithUrl:url
               refreshCache:refreshCache
                     params:params
                   progress:nil
                    success:success
                       fail:fail];
}

+ (TURLSessionTask *)getWithUrl:(NSString *)url
                     refreshCache:(BOOL)refreshCache
                           params:(NSDictionary *)params
                         progress:(TGetProgress)progress
                          success:(TRequestSuccessBlock)success
                             fail:(TRequestFailureBlock)fail {
    return [self _requestWithUrl:url
                    refreshCache:refreshCache
                       httpMedth:1
                          params:params
                        progress:progress
                         success:success
                            fail:fail];
}

+ (TURLSessionTask *)postWithUrl:(NSString *)url
                      refreshCache:(BOOL)refreshCache
                            params:(NSDictionary *)params
                           success:(TRequestSuccessBlock)success
                              fail:(TRequestFailureBlock)fail {
    return [self postWithUrl:url
                refreshCache:refreshCache
                      params:params
                    progress:nil
                     success:success
                        fail:fail];
}

+ (TURLSessionTask *)postWithUrl:(NSString *)url
                      refreshCache:(BOOL)refreshCache
                            params:(NSDictionary *)params
                          progress:(TPostProgress)progress
                           success:(TRequestSuccessBlock)success
                              fail:(TRequestFailureBlock)fail {
    return [self _requestWithUrl:url
                    refreshCache:refreshCache
                       httpMedth:2
                          params:params
                        progress:progress
                         success:success
                            fail:fail];
}


+ (TURLSessionTask *)_requestWithUrl:(NSString *)url
                          refreshCache:(BOOL)refreshCache
                             httpMedth:(NSUInteger)httpMethod
                                params:(NSDictionary *)params
                              progress:(TDownloadProgress)progress
                               success:(TRequestSuccessBlock)success
                                  fail:(TRequestFailureBlock)fail {
    if ([self shouldEncode]) {
        url = [self encodeUrl:url];
    }
    
    AFHTTPSessionManager *manager = [self manager];
    NSString *absolute = [self absoluteUrlWithPath:url];
    
    if ([self baseUrl] == nil) {
        if ([NSURL URLWithString:url] == nil) {
            NSLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
            return nil;
        }
    } else {
        NSURL *absoluteURL = [NSURL URLWithString:absolute];
        
        if (absoluteURL == nil) {
            NSLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
            return nil;
        }
    }
    
    TURLSessionTask *session = nil;
    
    if (httpMethod == 1) {
        if (sg_cacheGet) {
            if (sg_shoulObtainLocalWhenUnconnected) {
                if (sg_networkStatus == kTNetworkStatusNotReachable ||  sg_networkStatus == kTNetworkStatusStatusUnknown ) {
                    id response = [THttpTool cahceResponseWithURL:absolute
                                                           parameters:params];
                    if (response) {
                        if (success) {
                            success(response);
                        }
                        return nil;
                    }
                    else if(fail) {
                        NSError *error=[NSError new];
                        [error setValue:@"-1000" forKey:@"code"];
                        [error setValue:@"本地缓存读取失败" forKey:@"description"];
                        fail(error);
                    }
                }
            }
            if (!refreshCache) {// 获取缓存
                id response = [THttpTool cahceResponseWithURL:absolute
                                                       parameters:params];
                if (response) {
                    if (success) {
                        success(response);
                    }
                    return nil;
                }
                else if(fail) {
                    NSError *error=[NSError new];
                    [error setValue:@"-1000" forKey:@"code"];
                    [error setValue:@"本地缓存读取失败" forKey:@"description"];
                    fail(error);
                }

            }
        }
        

        session = [manager GET:url parameters:params headers:sg_httpHeaders progress:^(NSProgress * _Nonnull downloadProgress) {
            if (progress) {
                progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
            }
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            
            [self prettyPrintAboutHttpWithSessionTask:task withResponseObj:responseObject withManager:manager withParams:params];
            !success?:success(responseObject);
            if (sg_cacheGet) {
                [self cacheResponseObject:responseObject request:task.currentRequest parameters:params];
            }
            [[self allTasks] removeObject:task];
        
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [[self allTasks] removeObject:task];
            
            if ([error code] < 0 && sg_cacheGet) {// 获取缓存
                id response = [THttpTool cahceResponseWithURL:absolute
                                                       parameters:params];
                if (response) {
                    !success?:success(response);
                } else {
                    !fail?:fail(error);
                }
            } else {
                [self prettyPrintErrorAboutHttpWithSessionTask:task withError:error withManager:manager withParams:params];
                !fail?:fail(error);
            }
        }];
    } else if (httpMethod == 2) {//Post请求
        if (sg_cachePost ) {// 获取缓存
            if (sg_shoulObtainLocalWhenUnconnected) {
                if (sg_networkStatus == kTNetworkStatusNotReachable ||  sg_networkStatus == kTNetworkStatusStatusUnknown ) {
                    id response = [THttpTool cahceResponseWithURL:absolute
                                                           parameters:params];
                    if (response) {
                        !success?:success(response);
                        return nil;
                    }
                }
            }
            if (!refreshCache) {
                id response = [THttpTool cahceResponseWithURL:absolute
                                                       parameters:params];
                if (response) {
                    if (success) {
                        !success?:success(response);
                    }
                    return nil;
                }
            }
        }
        
        session = [manager POST:url parameters:params headers:sg_httpHeaders progress:^(NSProgress * _Nonnull downloadProgress) {
            if (progress) {
                progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
            }
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            [self prettyPrintAboutHttpWithSessionTask:task withResponseObj:responseObject withManager:manager withParams:params];
            !success?:success(responseObject);
            
            if (sg_cachePost) {
                [self cacheResponseObject:responseObject request:task.currentRequest  parameters:params];
            }
            
            [[self allTasks] removeObject:task];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [[self allTasks] removeObject:task];
            
            if ([error code] < 0 && sg_cachePost) {// 获取缓存
                id response = [THttpTool cahceResponseWithURL:absolute
                                                       parameters:params];
                
                if (response) {
                    !success?:success(response);
                } else {
                }
            } else {
                [self prettyPrintErrorAboutHttpWithSessionTask:task withError:error withManager:manager withParams:params];
                !fail?:fail(error);
            }
        }];
    }
    
    if (session) {
        [[self allTasks] addObject:session];
    }
    
    return session;
}


#pragma mark - post 上传文件-
+(void)uploadFileWithUrl:(NSString *)url WithData:(NSData *)fileData params:(id)params fileName:(NSString *)fileName mineType:(NSString *)mimeType progress:(void (^)(NSProgress *uploadProgress))uploadProgress success:(TRequestSuccessBlock)successHandler failure:(TRequestFailureBlock)failureHandler
{
    if (!fileData) {
        [self prettyPrintErrorWithUrl:url withErrorMessage:@"文件数据为空" withParams:params];
    }
    else{
        
        AFHTTPSessionManager *manager = [self manager];
        
        [manager POST:url parameters:params headers:sg_httpHeaders constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            [formData appendPartWithFileData:fileData name:@"file" fileName:fileName mimeType:mimeType];
        } progress:^(NSProgress * _Nonnull gress) {
            !uploadProgress?:uploadProgress(gress);
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            [self prettyPrintAboutHttpWithSessionTask:task withResponseObj:responseObject withManager:manager withParams:params];
            !successHandler?:successHandler(responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [self prettyPrintErrorAboutHttpWithSessionTask:task withError:error withManager:manager withParams:params];
            !failureHandler?:failureHandler(error);
        }];
    }
}

+(void)uploadFileWithUrl:(NSString *)url WithDataArray:(NSArray *)fileDataArray params:(id)params fileNames:(NSArray *)fileNames mineType:(NSString *)mimeType progress:(void (^)(NSProgress *uploadProgress))uploadProgress success:(TRequestSuccessBlock)successHandler failure:(TRequestFailureBlock)failureHandler
{
    if (fileDataArray.count==0) {
        [self prettyPrintErrorWithUrl:url withErrorMessage:@"文件数据为空" withParams:params];
    }
    else if(fileDataArray.count!=fileNames.count){
        [self prettyPrintErrorWithUrl:url withErrorMessage:@"文件数量与文件名数量不对应" withParams:params];
    }
    else{
        
        AFHTTPSessionManager *manager = [self manager];
        
        [manager POST:url parameters:params headers:sg_httpHeaders constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            [fileDataArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                [formData appendPartWithFileData:obj name:@"file" fileName:[fileNames objectAtIndex:idx] mimeType:mimeType];
            }];
            
        } progress:^(NSProgress * _Nonnull gress) {
            !uploadProgress?:uploadProgress(gress);
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            [self prettyPrintAboutHttpWithSessionTask:task withResponseObj:responseObject withManager:manager withParams:params];
            !successHandler?:successHandler(responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [self prettyPrintErrorAboutHttpWithSessionTask:task withError:error withManager:manager withParams:params];
            !failureHandler?:failureHandler(error);
        }];
    }
}


+ (TURLSessionTask *)downloadWithUrl:(NSString *)url
                          saveToPath:(NSString *)saveToPath
                            progress:(TDownloadProgress)progressBlock
                             success:(TRequestSuccessBlock)success
                             failure:(TRequestFailureBlock)failure {
    if ([self baseUrl] == nil) {
        if ([NSURL URLWithString:url] == nil) {
            NSLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
            return nil;
        }
    } else {
        if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
            NSLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
            return nil;
        }
    }
    
    NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    AFHTTPSessionManager *manager = [self manager];
    
    TURLSessionTask *session = nil;
    
    session = [manager downloadTaskWithRequest:downloadRequest progress:^(NSProgress * _Nonnull downloadProgress) {
        if (progressBlock) {
            progressBlock(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
        }
    } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        return [NSURL fileURLWithPath:saveToPath];
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        [[self allTasks] removeObject:session];
        
        if (error == nil) {
            !success?:success(filePath.absoluteString);
        } else {
            !failure?:failure(error);
        }
    }];
    
    [session resume];
    if (session) {
        [[self allTasks] addObject:session];
    }
    
    return session;
}




#pragma mark- 网络监测
+ (void)detectNetwork {
    AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    
    [reachabilityManager startMonitoring];
    [reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        if (status == AFNetworkReachabilityStatusNotReachable){
            sg_networkStatus = kTNetworkStatusStatusUnknown;
        } else if (status == AFNetworkReachabilityStatusUnknown){
            sg_networkStatus = kSCBondStatusUnknown;
        } else if (status == AFNetworkReachabilityStatusReachableViaWWAN){
            sg_networkStatus = kTNetworkStatusReachableViaWWAN;
        } else if (status == AFNetworkReachabilityStatusReachableViaWiFi){
            sg_networkStatus = kTNetworkStatusReachableViaWiFi;
        }
    }];
}


#pragma mark- 缓存配置
+ (void)obtainDataFromLocalWhenNetworkUnconnected:(BOOL)shouldObtain {
    sg_shoulObtainLocalWhenUnconnected = shouldObtain;
    if (sg_shoulObtainLocalWhenUnconnected && (sg_cacheGet || sg_cachePost)) {
        [self detectNetwork];
    }
}

+ (void)cacheGetRequest:(BOOL)isCacheGet shoulCachePost:(BOOL)shouldCachePost {
    sg_cacheGet = isCacheGet;
    sg_cachePost = shouldCachePost;
}
static inline NSString *cachePath() {
    return [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/TNetworkingCaches"];
}
+ (unsigned long long)totalCacheSize {
    NSString *directoryPath = cachePath();
    BOOL isDir = NO;
    unsigned long long total = 0;
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) {
        if (isDir) {
            NSError *error = nil;
            NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error];
            
            if (error == nil) {
                for (NSString *subpath in array) {
                    NSString *path = [directoryPath stringByAppendingPathComponent:subpath];
                    NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path
                                                                                          error:&error];
                    if (!error) {
                        total += [dict[NSFileSize] unsignedIntegerValue];
                    }
                }
            }
        }
    }
    
    return total;
}
+ (void)autoToClearCacheWithLimitedToSize:(NSUInteger)mSize {
    sg_maxCacheSize = mSize;
}
+ (void)clearCaches {
    NSString *directoryPath = cachePath();
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
        NSError *error = nil;
        [[NSFileManager defaultManager] removeItemAtPath:directoryPath error:&error];
        
        if (error) {
            NSLog(@"TNetworking clear caches error: %@", error);
        } else {
            NSLog(@"TNetworking clear caches ok");
        }
    }
}
+ (id)cahceResponseWithURL:(NSString *)url parameters:params {
    id cacheData = nil;
    
    if (url) {
        // Try to get datas from disk
        NSString *directoryPath = cachePath();
        NSString *absoluteURL = [self generateGETAbsoluteURL:url params:params];
        NSString *key = [NSString hybnetworking_md5:absoluteURL];
        NSString *path = [directoryPath stringByAppendingPathComponent:key];
        
        NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
        if (data) {
            cacheData = data;
            NSLog(@"Read data from cache for url: %@\n", url);
        }
    }
    
    return cacheData;
}

+ (void)cacheResponseObject:(id)responseObject request:(NSURLRequest *)request parameters:params {
    if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {
        NSString *directoryPath = cachePath();
        
        NSError *error = nil;
        
        if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
            [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
                                      withIntermediateDirectories:YES
                                                       attributes:nil
                                                            error:&error];
            if (error) {
#if DEBUG
                NSLog(@"create cache dir error: %@\n", error);
#endif
                return;
            }
        }
        
        NSString *absoluteURL = [self generateGETAbsoluteURL:request.URL.absoluteString params:params];
        NSString *key = [NSString hybnetworking_md5:absoluteURL];
        NSString *path = [directoryPath stringByAppendingPathComponent:key];
        NSDictionary *dict = (NSDictionary *)responseObject;
        
        NSData *data = nil;
        if ([dict isKindOfClass:[NSData class]]) {
            data = responseObject;
        } else {
            data = [NSJSONSerialization dataWithJSONObject:dict
                                                   options:NSJSONWritingPrettyPrinted
                                                     error:&error];
        }
        
        if (data && error == nil) {
            BOOL isOk = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
            if (isOk) {
                NSLog(@"cache file ok for request: %@\n", absoluteURL);
            } else {
                NSLog(@"cache file error for request: %@\n", absoluteURL);
            }
        }
    }
}

+ (NSString *)absoluteUrlWithPath:(NSString *)path {
    if (path == nil || path.length == 0) {
        return @"";
    }
    
    if ([self baseUrl] == nil || [[self baseUrl] length] == 0) {
        return path;
    }
    
    NSString *absoluteUrl = path;
    
    if (![path hasPrefix:@"http://"] && ![path hasPrefix:@"https://"]) {
        if ([[self baseUrl] hasSuffix:@"/"]) {
            if ([path hasPrefix:@"/"]) {
                NSMutableString * mutablePath = [NSMutableString stringWithString:path];
                [mutablePath deleteCharactersInRange:NSMakeRange(0, 1)];
                absoluteUrl = [NSString stringWithFormat:@"%@%@",
                               [self baseUrl], mutablePath];
            } else {
                absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
            }
        } else {
            if ([path hasPrefix:@"/"]) {
                absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
            } else {
                absoluteUrl = [NSString stringWithFormat:@"%@/%@",
                               [self baseUrl], path];
            }
        }
    }
    
    return absoluteUrl;
}





#pragma mark - Private
+ (AFHTTPSessionManager *)manager {
    @synchronized (self) {
        // 只要不切换baseurl，就一直使用同一个session manager
        if (sg_sharedManager == nil || sg_isBaseURLChanged) {
            // 开启转圈圈
            [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
            
            AFHTTPSessionManager *manager = nil;;
            if ([self baseUrl] != nil) {
                manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:[self baseUrl]]];
            } else {
                manager = [AFHTTPSessionManager manager];
            }
            
            switch (sg_requestType) {
                case kTRequestTypeJSON: {
                    manager.requestSerializer = [AFJSONRequestSerializer serializer];
                    break;
                }
                case kTRequestTypePlainText: {
                    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
                    break;
                }
                default: {
                    break;
                }
            }
            
            switch (sg_responseType) {
                case kTResponseTypeJSON: {
                    manager.responseSerializer = [AFJSONResponseSerializer serializer];
                    break;
                }
                case kTResponseTypeXML: {
                    manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
                    break;
                }
                case kTResponseTypeData: {
                    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
                    break;
                }
                default: {
                    break;
                }
            }
            
            manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
            
            
            for (NSString *key in sg_httpHeaders.allKeys) {
                if (sg_httpHeaders[key] != nil) {
                    [manager.requestSerializer setValue:sg_httpHeaders[key] forHTTPHeaderField:key];
                }
            }
            
            manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
                                                                                      @"text/html",
                                                                                      @"text/json",
                                                                                      @"text/plain",
                                                                                      @"text/javascript",
                                                                                      @"text/xml",
                                                                                      @"image/*"]];
            
            manager.requestSerializer.timeoutInterval = sg_timeout;
            
            // 设置允许同时最大并发数量，过大容易出问题
            manager.operationQueue.maxConcurrentOperationCount = 3;
            sg_sharedManager = manager;
        }
    }
    
    return sg_sharedManager;
}
+ (BOOL)shouldEncode {
    return sg_shouldAutoEncode;
}
+ (NSString *)encodeUrl:(NSString *)url {
    return [self hyb_URLEncode:url];
}
+ (NSString *)hyb_URLEncode:(NSString *)url {
    return [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
+ (NSMutableArray *)allTasks {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (sg_requestTasks == nil) {
            sg_requestTasks = [[NSMutableArray alloc] init];
        }
    });
    
    return sg_requestTasks;
}

+ (void)cancelAllRequest {
    @synchronized(self) {
        [[self allTasks] enumerateObjectsUsingBlock:^(TURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([task isKindOfClass:[TURLSessionTask class]]) {
                [task cancel];
            }
        }];
        
        [[self allTasks] removeAllObjects];
    };
}
// 仅对一级字典结构起作用
+ (NSString *)generateGETAbsoluteURL:(NSString *)url params:(id)params {
    if (params == nil || ![params isKindOfClass:[NSDictionary class]] || [params count] == 0) {
        return url;
    }
    
    NSString *queries = @"";
    for (NSString *key in params) {
        id value = [params objectForKey:key];
        
        if ([value isKindOfClass:[NSDictionary class]]) {
            continue;
        } else if ([value isKindOfClass:[NSArray class]]) {
            continue;
        } else if ([value isKindOfClass:[NSSet class]]) {
            continue;
        } else {
            queries = [NSString stringWithFormat:@"%@%@=%@&",
                       (queries.length == 0 ? @"&" : queries),
                       key,
                       value];
        }
    }
    
    if (queries.length > 1) {
        queries = [queries substringToIndex:queries.length - 1];
    }
    
    if (([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) && queries.length > 1) {
        if ([url rangeOfString:@"?"].location != NSNotFound
            || [url rangeOfString:@"#"].location != NSNotFound) {
            url = [NSString stringWithFormat:@"%@%@", url, queries];
        } else {
            queries = [queries substringFromIndex:1];
            url = [NSString stringWithFormat:@"%@?%@", url, queries];
        }
    }
    
    return url.length == 0 ? queries : url;
}

#pragma mark- 打印url 和 json数据 httpHeader用于调试
+(void)prettyPrintAboutHttpWithSessionTask:(NSURLSessionDataTask *)task withResponseObj:(id)responseObj withManager:(AFHTTPSessionManager *)mgr withParams:(id)params
{
#if DEBUG
    
    //    NSDictionary *dict = mgr.requestSerializer.HTTPRequestHeaders;
    NSData *dataJson = [NSJSONSerialization dataWithJSONObject:responseObj options:NSJSONWritingPrettyPrinted error:nil];
    NSString *jsonStr = [[NSString alloc] initWithData:dataJson encoding:NSUTF8StringEncoding];
    NSString *paramStr=@"无";
    if (params) {
        NSData *paramJson = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];
        paramStr = [[NSString alloc] initWithData:paramJson encoding:NSUTF8StringEncoding];
    }
    NSLog(@"\n\n\n"
          "\nT【成功】============================================\n"
          "【接口地址】%@\n"
          "【请 求 头】\n%@\n"
          "【请求参数】\n %@\n"
          "【请求结果】\n %@\n"
          "【Curl测试】\ncurl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' '%@?%@'\n"//form形式
//          "【Curl测试】\ncurl -X POST %@ -H  'accept: application/json' -H  'content-type: application/json' -d %@\n"//json形式
          "END=============================================\n"
          "\n\n\n"
          , task.currentRequest.URL.path
          , mgr.requestSerializer.HTTPRequestHeaders.description
          , paramStr
          , jsonStr
          , task.currentRequest.URL.absoluteString
          , [[[[[[[[paramStr stringByReplacingOccurrencesOfString:@"\"" withString:@""]stringByReplacingOccurrencesOfString:@":" withString:@"="]stringByReplacingOccurrencesOfString:@"{" withString:@""]stringByReplacingOccurrencesOfString:@"}" withString:@""]stringByReplacingOccurrencesOfString:@"," withString:@"&"]stringByReplacingOccurrencesOfString:@"\n" withString:@""]stringByReplacingOccurrencesOfString:@"  " withString:@""]stringByReplacingOccurrencesOfString:@" " withString:@""]//form形式
//          , paramStr//json形式
          );
#endif
}
+(void)prettyPrintErrorAboutHttpWithSessionTask:(NSURLSessionDataTask *)task withError:(NSError *)error withManager:(AFHTTPSessionManager *)mgr withParams:(id)params
{
#if DEBUG
    // 请求的header信息
    //    NSDictionary *dict = mgr.requestSerializer.HTTPRequestHeaders;
    //    NSLog(@"T*请求头: %@", dict);
    // 请求结果
    
    NSString *paramStr=@"无";
    if (params) {
        NSData *paramJson = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];
        paramStr = [[NSString alloc] initWithData:paramJson encoding:NSUTF8StringEncoding];
    }
    NSLog(@"\n"
          "\n【T失败】**************************************\n"
          "【接口地址】%@\n"
          "【请 求 头】\n%@\n"
          "【请求参数】\n %@\n"
          "【Curl测试】\ncurl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' '%@?%@'\n"//form形式
//          "【Curl测试】\ncurl -X POST %@ -H  'accept: application/json' -H  'content-type: application/json' -d %@\n"//json形式
          "【错误日志】\n %@\n"
         
          "END**************************************\n"
          "\n\n\n"
          , task.currentRequest.URL.path
          , mgr.requestSerializer.HTTPRequestHeaders.description
          , paramStr
          , task.currentRequest.URL.absoluteString
          , [[[[[[[[paramStr stringByReplacingOccurrencesOfString:@"\"" withString:@""]stringByReplacingOccurrencesOfString:@":" withString:@"="]stringByReplacingOccurrencesOfString:@"{" withString:@""]stringByReplacingOccurrencesOfString:@"}" withString:@""]stringByReplacingOccurrencesOfString:@"," withString:@"&"]stringByReplacingOccurrencesOfString:@"\n" withString:@""]stringByReplacingOccurrencesOfString:@"  " withString:@""]stringByReplacingOccurrencesOfString:@" " withString:@""]//form形式
//          , paramStr//json形式
          , error.code==-1011?[[NSString alloc] initWithData:[error.userInfo objectForKey:@"com.alamofire.serialization.response.error.data"] encoding:NSUTF8StringEncoding]:error.description
          );
#endif
    
}
+(void)prettyPrintErrorWithUrl:(NSString *)url withErrorMessage:(NSString *)errorMsg withParams:(id)params
{
#if DEBUG
    // 请求的header信息
    //    NSDictionary *dict = mgr.requestSerializer.HTTPRequestHeaders;
    //    NSLog(@"T*请求头: %@", dict);
    // 请求结果
    
    NSString *paramStr=@"无";
    if (params) {
        NSData *paramJson = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];
        paramStr = [[NSString alloc] initWithData:paramJson encoding:NSUTF8StringEncoding];
    }
    NSLog(@"\n"
          "\n【T失败】**************************************\n"
          "【接口地址】%@\n"
          //          "【请 求 头】\n%@\n"
          "【请求参数】\n %@\n"
          "【错误日志】\n %@\n"
          
          
          "END**************************************\n"
          "\n\n\n"
          , url
          //          , dict
          , paramStr
          , errorMsg
          
          );
#endif
    
}
@end
